package org.yaac.shared.property;

import static com.google.common.base.Strings.isNullOrEmpty;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

import org.yaac.shared.egql.ResultCell;
import org.yaac.shared.egql.ResultCell.ResultCellFactory;



/**
 * PropertyInfo is used as both entity properties and 
 * 
 * @author Max Zhu (thebbsky@gmail.com)
 *
 */
public abstract class PropertyInfo implements Comparable<PropertyInfo>, Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * @author Max Zhu (thebbsky@gmail.com)
	 *
	 */
	public static class Builder {
		
		/**
		 * reflection is not supported by GWT, 
		 * we have to use this way to deserialize PropertyInfo from ResultCell
		 * 
		 * @param resultCell
		 * @return
		 */
		public static PropertyInfo fromResultCell(ResultCell resultCell) {
			PropertyType type = resultCell.getType();
			List<String> params = resultCell.getPayload();
			List<ResultCell> children = resultCell.getChildren();
			ResultCell parent = resultCell.getParent();
			
			PropertyInfo resultInfo;
			
			switch (type) {
			case BOOL:
				resultInfo = new BooleanPropertyInfo(Boolean.valueOf(params.get(0)));
				break;
			case LONG:
				resultInfo = new LongPropertyInfo(Long.valueOf(params.get(0)));
				break;
			case DOUBLE:
				resultInfo = new DoublePropertyInfo(Double.valueOf(params.get(0)));
				break;
			case GEOPT:
				resultInfo = new GeoPtPropertyInfo(Float.valueOf(params.get(0)), Float.valueOf(params.get(1)));
				break;
			case KEY:
				KeyInfo parentKey = null;
				if (parent != null) {
					parentKey = (KeyInfo) fromResultCell(parent);
				}
				
				Long id = isNullOrEmpty(params.get(2)) ? null : Long.valueOf(params.get(2));
				
				resultInfo = new KeyInfo(parentKey, params.get(0), params.get(1), id, params.get(3));
				break;
			case NULL:
				resultInfo = new NullPropertyInfo();
				break;
			case STRING:
				resultInfo = new StringPropertyInfo(params.get(0));
				break;
			case TIMESTAMP:
				resultInfo = new DatePropertyInfo(new Date(Long.valueOf(params.get(0))));
				break;
			case USER:
				resultInfo = new UserPropertyInfo(
						params.get(0), params.get(1), params.get(2), params.get(3), params.get(4));
				break;
			case IM_HANDLE:
				resultInfo = new IMHandlePropertyInfo(params.get(0), params.get(1));
				break;
			case BLOB:
				resultInfo = new BlobPropertyInfo(Integer.valueOf(params.get(0)), params.get(1));
				break;
			case BLOB_KEY:
				resultInfo = new BlobKeyPropertyInfo(params.get(0));
				break;
			case TEXT:
				resultInfo = new TextPropertyInfo(params.get(0), Integer.valueOf(params.get(1)), params.get(2));
				break;
			case LIST:
				ListPropertyInfo result = new ListPropertyInfo();
				for (ResultCell child : children) {
					result.add(fromResultCell(child));
				}
				resultInfo = result;
				break;
			case CATEGORY:
				resultInfo = new CategoryPropertyInfo(params.get(0));
				break;
			case EMAIL:
				resultInfo = new EmailPropertyInfo(params.get(0));
				break;
			case LINK:
				resultInfo = new LinkPropertyInfo(params.get(0));
				break;
			case PHONE_NO:
				resultInfo = new PhoneNumberPropertyInfo(params.get(0));
				break;
			case POSTTAL_ADDRESS:
				resultInfo = new PostalAddressPropertyInfo(params.get(0));
				break;
			case SHORT_BLOB:
				resultInfo = new ShortBlobPropertyInfo(params.get(0));
				break;
			default:
				throw new IllegalArgumentException("Not a valid property type : " + type);
			}
			
			resultInfo.setTitle(resultCell.getTitle());
			resultInfo.setWarnings(resultCell.getWarnings());
			return resultInfo;
		}	
	}
	
	public static class FilePlaceHolder implements Serializable {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		
		private int currIdx;
		
		public FilePlaceHolder() {
			currIdx = 0;
		}
		
		public int next() {
			return ++currIdx;
		}
	}
	
	/**
	 * title of the property, can be entity property name, or derived property name, like sum(*)
	 */
	private String title;

	/**
	 * warnings when evaluating this property
	 * 
	 * this is always null / empty for direct datastore load properties
	 */
	private List<String> warnings;
	
	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}
	
	public List<String> getWarnings() {
		return warnings;
	}

	public void setWarnings(List<String> warnings) {
		this.warnings = warnings;
	}

	/**
	 * used to parse the property info to params list and sent via autobean proxy
	 * 
	 * ResultCellFactory is parsed as input parameter because the construction of AutoBeans are different 
	 * between client side and server side 
	 * 
	 * @param factory
	 * @return
	 */
	public ResultCell populateResultCell(ResultCellFactory factory) {
		ResultCell cell = factory.create();
		cell.setTitle(this.title);
		cell.setWarnings(this.warnings);
		return cell;
	}
	
	/**
	 * used in client side to render SafeHtmlCell
	 * 
	 * @return
	 */
	public abstract String asHtml();

	/**
	 * used in client side to re-generate expression on edit
	 * 
	 * @param placeHolder used to generate file param place holder
	 * @return
	 */
	public abstract String asExpression(FilePlaceHolder placeHolder);
	
	/**
	 * used in editor panel to generate temporary download paths
	 * 
	 * @return
	 */
	public abstract List<String> downloadPaths();
}
